/* eslint-disable jsdoc/no-restricted-syntax */
"use strict";

// MyCustomHMRPlugin.js
const fs = require("node:fs");
const path = require("node:path");
const { EntryPlugin, RuntimeGlobals, RuntimeModule } = require("webpack");

/**
 * @import { Compiler, WebpackPluginInstance} from "webpack";
 */

/**
 * @implements {WebpackPluginInstance}
 */
class MyCustomHMRPlugin {
  /**
   * @param {{ hmrHelperPath?: string, chunkName?: string }} options
   */
  constructor(options = {}) {
    this.hmrHelperPath =
      options.hmrHelperPath || path.resolve(__dirname, "my-hmr-helper.js");
    this.chunkName = options.chunkName || "my-custom-hmr-runtime"; // 用于标识这个运行时模块的 chunk 名称
  }

  /**
   * @param {Compiler} compiler
   */
  apply(compiler) {
    compiler.hooks.normalModuleFactory.tap(
      "MyCustomHMRPlugin",
      (normalModuleFactory) => {
        // 确保你的 HMR 助手模块被正确解析
        // normalModuleFactory.hooks.resolve.tap(
        //   "MyCustomHMRPlugin",
        //   (result, options) => {
        //     if (result.request === this.hmrHelperPath) {
        //       result.resolveOptions.paths = [compiler.context];
        //     }
        //     return result;
        //   }
        // );
      },
    );
    compiler.hooks.thisCompilation.tap("MyCustomHMRPlugin", (compilation) => {
      // 1. 添加一个“虚拟”入口，确保文件被处理为模块
      // 这样做可以确保 webpack 会解析和处理你的 my-hmr-helper.js
      compilation.hooks.addEntry.tap("MyCustomHMRPlugin", (entry, options) => {
        if (options.name === this.chunkName) {
          // 防止重复添加，只添加一次
          return;
        }
        // 使用一个独特的名称，这样它不会与其他入口混淆
        compilation.addEntry(
          compiler.context,
          EntryPlugin.createDependency(this.hmrHelperPath, this.chunkName),
          "MyCustomHMRPlugin",
          (/** @type {any} */ err) => {
            if (err) throw err;
          },
        );
      });

      // 2. 将这个模块作为运行时依赖项注入到每个入口 chunk
      // 这样可以确保你的 HMR 助手代码总是存在于所有包含运行时的 chunk 中
      compilation.hooks.optimizeChunks.tap("MyCustomHMRPlugin", (chunks) => {
        const runtimeChunk = (() => {
          for (const chunk of chunks) {
            if (chunk.name === this.chunkName) {
              return chunk;
            }
          }
        })();

        if (!runtimeChunk) {
          // 如果没有找到我们的运行时 chunk，可能是因为 addEntry 失败或被优化掉了
          return;
        }

        for (const chunk of chunks) {
          // 确保只有包含运行时代码的 chunk 才包含我们的 HMR 助手
          if (chunk.hasRuntime()) {
            // 添加为运行时模块，webpack 会确保它只被加载一次
            chunk.addModule(runtimeChunk.entryModule);
          }
        }
      });

      // 3. (可选但推荐) 使用 RuntimePlugin 钩子，确保 HMR helper 模块的执行顺序
      // 确保你的 HMR 逻辑在 webpack 的 HMR 运行时之后执行
      compilation.hooks.runtimeModule.tap(
        "MyCustomHMRPlugin",
        (runtimeModule, chunk) => {
          // 仅对我们的运行时模块进行处理
          if (runtimeModule.name === `webpack/runtime/${this.chunkName}`) {
            // 如果你的 helper 依赖于特定的 webpack 运行时全局变量，
            // 你可能需要确保这些变量在你的模块之前被定义。
            // 这是一个高级用例，需要了解 webpack 内部运行时模块的生成顺序。
            // 通常，如果你只是简单的 console.log 或监听 HMR 事件，这可能不是必需的。
          }
        },
      );
    });
  }
}

module.exports = MyCustomHMRPlugin;
